home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1999 January / EnigmA AMIGA RUN 33 (1999)(G.R. Edizioni)(IT)[!][issue 1999-01].iso / earcd / faq / computer-lang / ada / programming / part2.z / part2
Internet Message Format  |  1999-01-01  |  27KB

  1. Path: senator-bedfellow.mit.edu!bloom-beacon.mit.edu!newsfeed.internetmci.com!howland.reston.ans.net!surfnet.nl!swsbe6.switch.ch!swidir.switch.ch!epflnews!dinews.epfl.ch!Magnus.Kempe
  2. From: Magnus.Kempe@di.epfl.ch (Magnus Kempe)
  3. Newsgroups: comp.lang.ada,comp.answers,news.answers
  4. Subject: Ada FAQ: Programming with Ada (part 2 of 4)
  5. Followup-To: poster
  6. Date: 30 May 1996 17:08:50 GMT
  7. Organization: None
  8. Lines: 642
  9. Sender: magnus@lglsun4.epfl.ch (Magnus Kempe)
  10. Approved: news-answers-request@MIT.EDU
  11. Distribution: world
  12. Message-ID: <4okkn2$idt@disunms.epfl.ch>
  13. Reply-To: Magnus.Kempe@di.epfl.ch (Magnus Kempe)
  14. NNTP-Posting-Host: lglsun4.epfl.ch
  15. Mime-Version: 1.0
  16. Content-Type: text/plain; charset=iso-8859-1
  17. Content-Transfer-Encoding: 8bit
  18. Summary: Ada Programmer's Frequently Asked Questions (and answers),
  19.            part 2 of 4.
  20.          Please read before posting.
  21. Keywords: advanced language, artificial languages, computer software,
  22.           data processing, programming languages, Ada
  23. Xref: senator-bedfellow.mit.edu comp.lang.ada:45674 comp.answers:18997 news.answers:73083
  24.  
  25. Archive-name: computer-lang/Ada/programming/part2
  26. Comp-lang-ada-archive-name: programming/part2
  27. Posting-Frequency: monthly
  28. Last-modified: 22 May 1996
  29. Last-posted: 23 April 1996
  30.  
  31.                                Ada Programmer's
  32.                        Frequently Asked Questions (FAQ)
  33.  
  34.    IMPORTANT NOTE: No FAQ can substitute for real teaching and
  35.    documentation. There is an annotated list of Ada books in the
  36.    companion comp.lang.ada FAQ.
  37.  
  38.     Recent changes to this FAQ are listed in the first section after the table
  39.     of contents. This document is under explicit copyright.
  40.  
  41. This is part 2 of a 4-part posting; part 1 contains the table of contents.
  42. Part 3 begins with question 6.
  43. Part 4 begins with question 9.
  44. Parts 3 and 4 should be the next postings in this thread.
  45. Part 1 should be the previous posting in this thread.
  46.  
  47.  
  48. 5: Object-Oriented Programming with Ada
  49.  
  50.  
  51. 5.1: Why does Ada have "tagged types" instead of classes?
  52.  
  53.    (Tucker Taft responds):
  54.  
  55.    Someone recently asked me to explain the difference between the
  56.    meaning of the term "class" in C++ and its meaning in Ada 9X. Here is
  57.    a synopsis of the answer:
  58.  
  59.    In C++, the term "class" refers to three different, but related
  60.    things:
  61.      * a language construct, that encapsulates the definitions of data
  62.        members, member functions, nested types, etc.;
  63.  
  64.      * a particular kind of type, defined by a class construct (or by
  65.        "struct" which is a special case of "class");
  66.  
  67.      * a set of types consisting of a type and all of its derivatives,
  68.        direct and indirect.
  69.  
  70.  
  71.    In Ada 9X, the term "class" refers only to the third of the above
  72.    definitions. Ada 9X (and Ada 83) has three different terms for the
  73.    concepts corresponding to the above three things:
  74.      * a "package" encapsulates the definitions of types, objects,
  75.        operations, exceptions, etc which are logically related. (The
  76.        operations of a type defined immediately within the package where
  77.        the type is declared are called, in 9X, the "primitive operations"
  78.        of the type, and in some sense, define the "primitive" semantics
  79.        of the type, especially if it is a private type.)
  80.  
  81.      * a "type" is characterized by a set of values and a set of
  82.        primitive operations (there are a million definitions of "type,"
  83.        unfortunately, but you know what I mean...);
  84.  
  85.      * a "class" is a set of types with similar values and operations; in
  86.        particular, a type and and all of its derivatives, direct and
  87.        indirect, represents a (derivation) class. Also, the set of
  88.        integer types form the integer "class," and so on for the other
  89.        language-defined classes of types in the language.
  90.  
  91.  
  92.    Some OOP languages take an intermediary position. In CLOS, a "class"
  93.    is not an encapsulating construct (CLOS has "packages"). However, a
  94.    "class" is both a type and a set of types, depending on context.
  95.    (Methods "float" freely.)
  96.  
  97.    The distinction Ada 9X makes between types and classes (= set of
  98.    types) carries over into the semantic model, and allows some
  99.    interesting capabilities not present in C++. In particular, in Ada 9X
  100.    one can declare a "class-wide" object initialized by copy from a
  101.    "class-wide" formal parameter, with the new object carrying over the
  102.    underlying type of the actual parameter. For example:
  103.  
  104.      procedure Print_In_Bold (X : T'Class) is
  105.        -- Copy X, make it bold face, and then print it.
  106.        Copy_Of_X : T'Class := X;
  107.      begin
  108.         Make_Bold (Copy_Of_X);
  109.         Print (Copy_Of_X);
  110.      end P;
  111.  
  112.  
  113.    In C++, when you declare an object, you must specify the "exact" class
  114.    of the object -- it cannot be determined by the underlying class of
  115.    the initializing value. Implementing the above procedure in a general
  116.    way in C++ would be slightly more tedious.
  117.  
  118.    Similarly, in Ada 9X one can define an access type that designates
  119.    only one specific type, or alternatively, one can define one that can
  120.    designate objects of any type in a class (a "class-wide" access type).
  121.    For example:
  122.  
  123.      type Fancy_Window_Ptr is access Fancy_Window;
  124.        -- Only points at Fancy Windows -- no derivatives allowed
  125.      type Any_Window_Ptr is access Window'Class;
  126.        -- Points at Windows, and any derivatives thereof.
  127.  
  128.  
  129.    In C++, all pointers/references are "class-wide" in this sense; you
  130.    can't restrict them to point at only one "specific" type.
  131.  
  132.    In other words, C++ makes the distinction between "specific" and
  133.    "class-wide" based on pointer/reference versus object/value, whereas
  134.    in Ada 9X, this distinction is explicit, and corresponds to the
  135.    distinction between "type" (one specific type) and "class" (set of
  136.    types).
  137.  
  138.    The Ada 9X approach we believe (hope ;-) gives somewhat better control
  139.    over static versus dynamic binding, and is less error prone since it
  140.    is type-based, rather than being based on reference vs. value.
  141.  
  142.    In any case, in Ada 9X, C++, and CLOS it makes sense to talk about
  143.    "class libraries," since a given library will generally consist of a
  144.    set of interrelated types. In Ada 9X and CLOS, one could alternatively
  145.    talk about a set of "reusable packages" and mean essentially the same
  146.    thing.
  147.  
  148.  
  149. 5.2: Variant records seem like a dead feature now. When should I use them
  150. instead of tagged types?
  151.  
  152.    This is an instance of a much more general question: "When should I
  153.    use what kind of type?" The simple answer is: "When it makes sense to
  154.    do so." The real key to chosing a type in Ada is to look at the
  155.    application, and pick the type that most closely models the problem.
  156.  
  157.    For instance, if you are modelling data transmission where the message
  158.    packets may contain variable forms of data, a variant record --not a
  159.    hierarchy of tagged types-- is an appropriate model, since there may
  160.    be no relationship between the data items other than their being
  161.    transmitted over one channel. If you choose to model the base type of
  162.    the messages with a tagged type, that may present more problems than
  163.    it solves when communicating across distinct architectures.
  164.  
  165.    [More to be said about variant programming vs. incremental
  166.    programming.]
  167.  
  168.  
  169. 5.3: What is meant by "interface inheritance" and how does Ada support it?
  170.  
  171.    This answer intentionally left blank.
  172.  
  173.  
  174. 5.4: How do you do multiple inheritance in Ada 9X?
  175.  
  176.    There is a lengthy paper in file
  177.    ftp://sw-eng.falls-church.va.us/public/AdaIC/flyers/9xm-inh.txt
  178.  
  179.    That document describes several mechanisms for achieving MI in Ada. It
  180.    is not unusual, however, to find complaints about the syntax and the
  181.    perceived burden it places on the developer. This is what Tucker Taft
  182.    had to say when responging to such a criticism on comp.lang.ada:
  183.  
  184.    Coming up with a syntax for multiple inheritance was not the
  185.    challenge. The challenge was coming up with a set of straightforward
  186.    yet flexible rules for resolving the well known problems associated
  187.    with multiple inheritance, namely:
  188.      * If the same type appears as an ancestor more than once, should all
  189.        or some of its data components be duplicated, or shared? If any
  190.        are duplicated, how are they referenced unambiguously?
  191.  
  192.      * If the same-named (including same parameter/result profile)
  193.        operation is inherited along two paths, how is the ambiguity
  194.        resolved? Can you override each with different code? How do you
  195.        refer to them later?
  196.  
  197.      * Etc.
  198.  
  199.  
  200.    For answers, you can look at the various languages that define a
  201.    built-in approach to multiple inheritance. Unfortunately, you will
  202.    generally get a different answer for each language -- hardly a
  203.    situation that suggests we will be able to craft an international
  204.    consensus. Eiffel uses renaming and other techniques, which seem quite
  205.    flexible, but at least in some examples, can be quite confusing (where
  206.    you override "B" to change what "A" does in some distant ancestor).
  207.    C++ has both non-virtual and virtual base clases, with a number of
  208.    rules associated with each, and various limitations relating to
  209.    downcasting and virtual base classes. CLOS uses simple name matching
  210.    to control "slot" merging. Some languages require that all but one of
  211.    the parent types be abstract, data-less types, so only interfaces are
  212.    being inherited; however if the interfaces happen to collide, you
  213.    still can end up with undesirable and potentially unresolvable
  214.    collisions (where you really want different code for same-named
  215.    interfaces inherited from different ancestors).
  216.  
  217.    One argument is that collisions are rare to begin with, so it doesn't
  218.    make much different how they are resolved. That is probably true, but
  219.    the argument doesn't work too well during an open language design
  220.    process -- people get upset at the most unbelievably trivial and
  221.    rarely used features if not "correctly" designed (speaking from
  222.    experience here ;-).
  223.  
  224.    Furthermore, given that many of the predominant uses of MI (separation
  225.    of interface inheritance from implementation inheritance, gaining
  226.    convenient access to another class's features, has-a relationships
  227.    being coded using MI for convenience, etc.) are already handled very
  228.    well in Ada 9X, it is hard to justify getting into the MI language
  229.    design fray at all. The basic inheritance model in Ada 9X is simple
  230.    and elegant. Why clutter it up with a lot of relatively ad-hoc rules
  231.    to handle one particular approach to MI? For the rare cases where MI
  232.    is really critical, the last thing the programmer wants in the
  233.    language is the "wrong" MI approach built in.
  234.  
  235.    So the basic answer is that at this point in the evolution of OO
  236.    language design, it seemed wiser to provide MI building blocks, rather
  237.    than to foist the wrong approach on the programmer, and be regretting
  238.    it and working around it for years to come.
  239.  
  240.    Perhaps [Douglas Arndt] said it best...
  241.  
  242.      Final note: inheritance is overrated, especially MI. ...
  243.  
  244.  
  245.    If the only or primary type composition mechanism in the language is
  246.    based on inheritance, then by all means, load it up. But Ada 9X
  247.    provides several efficient and flexible type composition mechanisms,
  248.    and there is no need to overburden inheritance with unnecessary and
  249.    complicated baggage.
  250.  
  251.  
  252. 5.5: Why are Controlled types so, well, strange?
  253.  
  254.    (Tucker Taft responds):
  255.  
  256.    We considered many approaches to user-defined finalization and
  257.    user-defined assignment. Ada presents challenges that make it harder
  258.    to define assignment than in other languages, because assignment is
  259.    used implicitly in several operations (by-copy parameter passing,
  260.    function return, aggregates, object initialization, initialized
  261.    allocators, etc.), and because Ada has types whose set of components
  262.    can be changed as a result of an assignment.
  263.  
  264.    For example:
  265.  
  266.      type T (D : Boolean := False) is record
  267.        case D is
  268.          when False => null;
  269.          when True => H : In_Hands;
  270.        end case;
  271.      end record;
  272.  
  273.      X,Z : T;
  274.      Y : T := (True, H => ...);
  275.  
  276.      ...
  277.  
  278.      X := Y;   -- "X.H" component coming into existence
  279.      Y := Z;   -- "Y.H" component going out of existence
  280.  
  281.  
  282.    With a type like the one above, there are components that can come and
  283.    go as a result of assignment. The most obvious definition of
  284.    assignment would be:
  285.  
  286.      procedure ":=" (Left : in out In_Hands; Right : in In_Hands);
  287.  
  288.  
  289.    Unfortunately, this wouldn't work for the "H" component, because there
  290.    is no preexisting "In_Hands" component to be assigned into in the
  291.    first case, and in the second case, there is no "In_Hands" component
  292.    to assign "from."
  293.  
  294.    Therefore, we decided to decompose the operation of assignment into
  295.    separable pieces: finalization of the left hand side; simple copying
  296.    of the data from the right hand side to the left hand side; and then
  297.    adjustment of the new left hand side. Other decompositions are
  298.    probably possible, but they generally suffer from not being easily
  299.    composable, or not handling situations like the variant record above.
  300.  
  301.    Imagine a function named ":=" that returns a copy of its in parameter.
  302.    To do anything interesting it will have to copy the in parameter into
  303.    a local variable, and then "fiddle" with that local variable
  304.    (essentially what "Adjust" does), and then return that local variable
  305.    (which will make yet another copy). The returned result will have to
  306.    be put back into the desired place (which might make yet another
  307.    copy). For a large object, this might involve several extra copies.
  308.  
  309.    By having the user write just that part of the operation that
  310.    "fiddles" with the result after making a copy, we allow the
  311.    implementation to eliminate redundant copying. Furthermore, some
  312.    user-defined representations might be position dependent. That is, the
  313.    final "fiddling" has to take place on the object in its final
  314.    location. For example, one might want the object to point to itself.
  315.    If the implementation copies an object after the user code has
  316.    adjusted it, such self-references will no longer point to the right
  317.    place.
  318.  
  319.    So, as usual, once one gets into working out the details and all the
  320.    interactions, the "obvious" proposal (such as a procedure ":=") no
  321.    longer looks like the best answer, and the best answer one can find
  322.    potentially looks "clumsy" (at least before you try to work out the
  323.    details of the alternatives).
  324.  
  325.  
  326. 5.6: What do "covariance" and "contravariance" mean, and does Ada support
  327. either or both?
  328.  
  329.    (From Robert Martin) [This is C++ stuff, it should be completely
  330.    re-written for Ada. --MK]
  331.  
  332.  
  333.  R> covariance:  "changes with"
  334.  R> contravariance: "changes against"
  335.  
  336.  R> class A
  337.  R> {
  338.  R>    public:
  339.  R>      A* f(A*);   // method of class A, takes A argument and returns A
  340.  R>      A* g(A*);   // same.
  341.  R> };
  342.  
  343.  R> class B : public A // class B is a subclass of class A
  344.  R> {
  345.  R>   public:
  346.  R>     B* f(B*);  // method of class B overrides f and is covariant.
  347.  R>     A* g(A*);  // method of class B overrides g and is contravariant.
  348.  R> };
  349.  
  350.  R> The function f is covariant because the type of its return value and
  351.  R> argument changes with the class it belongs to.  The function g is
  352.  R> contravariant because the types of its return value and arguments does not
  353.  R> change with the class it belongs to.
  354.  
  355.  
  356.    Actually, I would call g() invariant. If you look in Sather, (one of
  357.    the principle languages with contravariance), you will see that the
  358.    method in the decendent class actually can have arguments that are
  359.    superclasses of the arguments of its parent. So for example:
  360.  
  361. class A : public ROOT
  362. {
  363.    public:
  364.      A* f(A*);   // method of class A, takes A argument and returns A
  365.      A* g(A*);   // same.
  366. };
  367.  
  368. class B : public A // class B is a subclass of class A
  369. {
  370.   public:
  371.     B* f(B*);  // method of class B overrides f and is covariant.
  372.     ROOT* g(ROOT*);  // method of class B overrides g and is contravariant.
  373. };
  374.  
  375.  
  376.    To my knowledge the uses for contravariance are rare or nonexistent.
  377.    (Anyone?). It just makes the rules easy for the compiler to type
  378.    check. On the other hand, co-variance is extremely useful. Suppose you
  379.    want to test for equality, or create a new object of the same type as
  380.    the one in hand:
  381.  
  382. class A
  383. {
  384.    public:
  385.       BOOLEAN equal(A*);
  386.       A* create();
  387. }
  388.  
  389. class B: public A
  390. {
  391.    public:
  392.       BOOLEAN equal(B*);
  393.       B* create();
  394. }
  395.  
  396.  
  397.    Here covariance is exactly what you want. Eiffel gives this to you,
  398.    but the cost is giving up 100% compile time type safety. This seem
  399.    necessary in cases like these.
  400.  
  401.    In fact, Eiffel gives you automatic ways to make a method covariant,
  402.    called "anchored types". So you could declare, (in C++/eiffese):
  403.  
  404. class A
  405. {
  406.    public:
  407.       BOOLEAN equal(like Current *);
  408.       like Current * create();
  409. }
  410.  
  411.  
  412.    Which says equal takes an argument the same type as the current
  413.    object, and create returns an object of the same type as current. Now,
  414.    there is not even any need to redeclare these in class B. Those
  415.    transformations happen for free!
  416.  
  417.  
  418. 5.7: What is meant by upcasting/expanding and downcasting/narrowing?
  419.  
  420.    (Tucker Taft replies):
  421.  
  422.    Here is the symmetric case to illustrate upcasting and downcasting.
  423.  
  424.      type A is tagged ...;   -- one parent type
  425.  
  426.      type B is tagged ...;   -- another parent type
  427.  
  428.      ...
  429.  
  430.      type C;   -- the new type, to be a mixture of A and B
  431.  
  432.      type AC (Obj : access C'Class) is
  433.        new A
  434.        with ...;
  435.        -- an extension of A to be mixed into C
  436.  
  437.      type BC (Obj : access C'Class) is
  438.        new B
  439.        with ...;
  440.        -- an extension of B to be mixed into C
  441.  
  442.      type C is
  443.        tagged limited record
  444.          A : AC (C'Access);
  445.          B : BC (C'Access);
  446.          ... -- other stuff if desired
  447.        end record;
  448.  
  449.  
  450.    We can now pass an object of type C to anything that takes an A or B
  451.    as follows (this presumes that Foobar and QBert are primitives of A
  452.    and B, respectively, so they are inherited; if not, then an explicit
  453.    conversion (upcast) to A and B could be used to call the original
  454.    Foobar and QBert).
  455.  
  456.      XC : C;
  457.    ...
  458.      Foobar (XC.A);
  459.      QBert (XC.B);
  460.  
  461.  
  462.    If we want to override what Foobar does, then we override Foobar on
  463.    AC. If we want to override what QBert does, then we override QBert on
  464.    BC.
  465.  
  466.    Note that there are no naming conflicts, since AC and BC are distinct
  467.    types, so even if A and B have same-named components or operations, we
  468.    can talk about them and/or override them individually using AC and BC.
  469.  
  470.  
  471.    Upcasting (from C to A or C to B) is trivial -- A(XC.A) upcasts to A;
  472.    B(XC.B) upcasts to B.
  473.  
  474.    Downcasting (narrowing) is also straightforward and safe. Presuming XA
  475.    of type A'Class, and XB of type B'Class:
  476.  
  477.      AC(XA).Obj.all downcasts to C'Class (and verifies XA in AC'Class)
  478.      BC(XB).Obj.all downcasts to C'Class (and verifies XB in BC'Class)
  479.  
  480.  
  481.    You can check before the downcast to avoid a Constraint_Error:
  482.  
  483.      if XA not in AC'Class then -- appropriate complaint
  484.  
  485.      if XB not in BC'Class then -- ditto
  486.  
  487.  
  488.    The approach is slightly simpler (though less symmetric) if we choose
  489.    to make A the "primary" parent and B a "secondary" parent:
  490.  
  491.      type A is ...
  492.      type B is ...
  493.  
  494.      type C;
  495.  
  496.      type BC (Obj : access C'Class) is
  497.        new B
  498.        with ...
  499.  
  500.      type C is
  501.        new A
  502.        with record
  503.          B : BC (C'Access);
  504.          ... -- other stuff if desired
  505.        end record;
  506.  
  507.  
  508.    Now C is a "normal" extension of A, and upcasting from C to A and
  509.    (checked) downcasting from C'Class to A (or A'Class) is done with
  510.    simple type conversions. The relationship between C and B is as above
  511.    in the symmetric approach.
  512.  
  513.    Not surprisingly, using building blocks is more work than using a
  514.    "builtin" approach for simple cases that happen to match the builtin
  515.    approach, but having building blocks does ultimately mean more
  516.    flexibility for the programmer -- there are many other structures that
  517.    are possible in addition to the two illustrated above, using the
  518.    access discriminant building block.
  519.  
  520.    For example, for mixins, each mixin "flavor" would have an access
  521.    discriminant already:
  522.  
  523.      type Window is ...  -- The basic "vanilla" window
  524.  
  525.      -- Various mixins
  526.      type Win_Mixin_1 (W : access Window'Class) is ...
  527.  
  528.      type Win_Mixin_2 (W : access Window'Class) is ...
  529.  
  530.      type Win_Mixin_3 (W : access Window'Class) is ...
  531.  
  532.  
  533.    Given the above vanilla window, plus any number of window mixins, one
  534.    can construct a desired window by including as many mixins as wanted:
  535.  
  536.      type My_Window is
  537.        new Window
  538.        with record
  539.          M1 : Win_Mixin_1 (My_Window'access);
  540.          M3 : Win_Mixin_3 (My_Window'access);
  541.          M11 : Win_Mixin_1(My_Window'access);
  542.          ... -- plus additional stuff, as desired.
  543.        end record;
  544.  
  545.  
  546.    As illustrated above, you can incorporate the same "mixin" multiple
  547.    times, with no naming conflicts. Every mixin can get access to the
  548.    enclosing object. Operations of individual mixins can be overridden by
  549.    creating an extension of the mixin first, overriding the operation in
  550.    that, and then incorporating that tweaked mixin into the ultimate
  551.    window.
  552.  
  553.    I hope the above helps better illustrate the use and flexibility of
  554.    the Ada 9X type composition building blocks.
  555.  
  556.  
  557. 5.8: How does Ada do "narrowing"?
  558.  
  559.    Dave Griffith said
  560.  
  561.      . . . Nonetheless, The Ada9x committee chose a structure-based
  562.      subtyping, with all of the problems that that is known to cause. As
  563.      the problems of structure based subtyping usually manifest only in
  564.      large projects maintained by large groups, this is _precisely_ the
  565.      subtype paradigm that Ada9x should have avoided. Ada9x's model is,
  566.      as Tucker Taft pointed out, quite easy to use for simple OO
  567.      programming. There is, however, no good reason to _do_ simple OO
  568.      programming. OO programmings gains click in somewhere around 10,000
  569.      LOC, with greatest gains at over 100,000. At these sizes, "just
  570.      declare it tagged" will result in unmaintainable messes. OO
  571.      programming in the large rapidly gets difficult with structure based
  572.      subtyping. Allowing by-value semantics for objects compounds these
  573.      problems. All of this is known. All of this was, seemingly, ignored
  574.      by Ada9x.
  575.  
  576.  
  577.    (Tucker Taft answers)
  578.  
  579.    As explained in a previous note, Ada 9X supports the ability to hide
  580.    the implementation heritage of a type, and only expose the desired
  581.    interface heritage. So we are not stuck with strictly "structure-based
  582.    subtyping." Secondly, by-reference semantics have many "well known"
  583.    problems as well, and the designers of Modula-3 chose to, seemingly,
  584.    ignore those ;-) ;-). Of course, in reality, neither set of language
  585.    designers ignored either of these issues. Language design involves
  586.    tradeoffs. You can complain we made the wrong tradeoff, but to
  587.    continue to harp on the claim that we "ignored" things is silly. We
  588.    studied every OOP language under the sun on which we could find any
  589.    written or electronic material. We chose value-based semantics for
  590.    what we believe are good reasons, based on reasonable tradeoffs.
  591.  
  592.    First of all, in the absence of an integrated garbage collector,
  593.    by-reference semantics doesn't make much sense. Based on various
  594.    tradeoffs, we decided against requiring an integrated garbage
  595.    collector for Ada 9X.
  596.  
  597.    Secondly, many of the "known" problems with by-value semantics we
  598.    avoided, by eliminating essentially all cases of "implicit
  599.    truncation." One of the problems with the C++ version of "value
  600.    semantics" is that on assignment and parameter passing, implicit
  601.    truncation can take place mysteriously, meaning that a value that
  602.    started its life representing one kind of thing gets truncated
  603.    unintentionally so that it looks like a value of some ancestor type.
  604.    This is largely because the name of a C++ class means differnt things
  605.    depending on the context. When you declare an object, the name of the
  606.    class determines the "exact class" of the object. The same thing
  607.    applies to a by-value parameter. However, for references and pointers,
  608.    the name of a class stands for that class and all of its derivatives.
  609.    But since, in C++, a value of a subclass is always acceptable where a
  610.    value of a given class is expected, you can get implicit truncation as
  611.    part of assignment and by-value parameter passing. In Ada 9X, we avoid
  612.    the implicit truncation because we support assignment for "class-wide"
  613.    types, which never implicitly truncates, and one must do an explicit
  614.    conversion to do an assignment that truncates. Parameter passing never
  615.    implicitly truncates, even if an implicit conversion is performed as
  616.    part of calling an inherited subprogram.
  617.  
  618.  
  619. 5.9: What is the difference between a class-wide access type and a "general"
  620. class-wide access type?
  621.  
  622.      What is exactly the difference between
  623.  
  624. type A is access Object'Class;
  625.  
  626.      and
  627.  
  628. type B is access all Object'Class;
  629.  
  630.      In the RM and Rationale only definitions like B are used. What's the
  631.      use for A-like definitions ?
  632.  
  633.  
  634.    (Tucker Taft answers)
  635.  
  636.    The only difference is that A is more restrictive, and so presumably
  637.    might catch bugs that B would not. A is a "pool-specific" access type,
  638.    and as such, you cannot convert values of other access types to it,
  639.    nor can you use 'Access to create values of type A. Values of type A
  640.    may only point into its "own" pool; that is only to objects created by
  641.    allocators of type A. This means that unchecked-deallocation is
  642.    somewhat safer when used with a pool-specific type like A.
  643.  
  644.    B is a "general" access type, and you can allocate in one storage
  645.    pool, and then convert the access value to type B and store it into a
  646.    variable of type B. Similarly, values of type B may point at objects
  647.    declared "aliased."
  648.  
  649.    When using class-wide pointer types, type conversion is sometimes used
  650.    for "narrowing." This would not in general be possible if you had left
  651.    out the "all" in the declaration, as in the declaration of A. So, as a
  652.    general rule, access-to-classwide types usually need to be general
  653.    access types. However, there is no real harm in starting out with a
  654.    pool-specific type, and then if you find you need to do a conversion
  655.    or use 'Access, the compiler should notify you that you need to add
  656.    the "all" in the declaration of the type. This way you get the added
  657.    safety of using a pool-specific access type, until you decide
  658.    explicitly that you need the flexibility of general access types.
  659.  
  660.    In some implementations, pool-specific access types might have a
  661.    shorter representation, since they only need to be able to point at
  662.    objects in a single storage pool. As we move toward 64-bit address
  663.    spaces, this might be a significant issue. I could imagine that
  664.    pool-specific access types might remain 32-bits in some
  665.    implementations, while general access types would necessarily be
  666.    64-bits.
  667.